package com.znpc;

import com.znpc.entity.Order;
import com.znpc.valmap.ValMap;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @Date 2021-08-19 13:26
 * @Author YangFan
 * @Description MainApplication
 */
public class MainApplication {

//    public static void main(String[] args) throws IOException, GRBException {
//
//        // 获取订单列表
//        List<Order> orderList = DataReader.getOrderList();
//
//        // 获取产能数据： 产品 : { 产线 : 产能 }
//        Map<String, Map<Integer, Integer>> capacityMap = DataReader.getCapacityMap();
//
//        // 获取基料换产评分： 后序基料 : { 前序基料 : 评分 }
//        Map<String, Map<String, Integer>> bmScoreMap = DataReader.getBMScoreMap();
//
//        int ordersNumber = orderList.size();
//        int linesNumber = 7;
//
//        // 求解器准备
//        System.out.println("1. 求解器准备 ......");
//        GRBEnv env = new GRBEnv(true);
//        env.start();
//        GRBModel model = new GRBModel(env);
//
//        System.out.println("2. 定义决策变量 ......");
//        // 索引 k 从 0 开始，对应产线编号从 1 开始，即 k 代表 k+1 号产线
//
//        // 决策变量 Vjk：订单 j 在 k 号产线上生产(0-不生产,1-生产)
//        GRBVar[][] Vjk = new GRBVar[ordersNumber][linesNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            for (int k = 0; k < linesNumber; k++) {
//                Vjk[j][k] = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, "V" + "_" + j + "_" + k);
//            }
//        }
//
//        // 决策变量 Xijk：产线 k 上订单 i 是否在订单 j 前生产(0-不生产,1-生产)
//        GRBVar[][][] Xijk = new GRBVar[ordersNumber][ordersNumber][linesNumber];
//        for (int i = 0; i < ordersNumber; i++) {
//            for (int j = 0; j < ordersNumber; j++) {
//                for (int k = 0; k < linesNumber; k++) {
//                    Xijk[i][j][k] = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, "X" + i + "_" + j + "_" + k);
//                }
//            }
//        }
//
//        // 决策变量 Cj：订单 j 生产完成时间
//        GRBVar[] Cj = new GRBVar[ordersNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            Cj[j] = model.addVar(0.0, 1500.0, 0.0, GRB.CONTINUOUS, "C" + j);
//        }
//
//        // 决策变量 Tj：订单 j 超期时间
//        GRBVar[] Tj = new GRBVar[ordersNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            Tj[j] = model.addVar(0.0, 1000, 0.0, GRB.CONTINUOUS, "T" + j);
//        }
//
//        System.out.println("3. 获取常量集合 ......");
//        // 常量集合 dj：订单 j 的交货日期
//        Integer[] dj = orderList.stream()
//                .map(order -> order.deliverDate)
//                .map(deliverDate -> ValMap.dateVal(deliverDate))
//                .toArray(Integer[]::new);
//
//        // 常量集合 ejk：订单 j 可以分配到 k 产线生产
//        int[][] ejk = new int[ordersNumber][linesNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            Set<Integer> lines = capacityMap.get(orderList.get(j).pName).keySet();
//            for (Integer line : lines) {
//                ejk[j][line - 1] = 1;
//            }
//        }
//
//
//        // 常量集合 rj：订单 j 原料到货时间
//        Integer[] rj = orderList.stream()
//                .map(order -> order.arriveDate)
//                .map(arriveDate -> ValMap.dateVal(arriveDate))
//                .toArray(Integer[]::new);
//
//
//        // 一个大的值
//        int M = 9999;
//
//        // 常量结合 pjk：订单 j 在 k 产线上的加工时间
//        float[][] pjk = new float[ordersNumber][linesNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            Order order = orderList.get(j);
//            Set<Integer> lines = capacityMap.get(order.pName).keySet();
//            for (int k = 0; k < linesNumber; k++) {
//                if (lines.contains(k + 1)) {
//                    pjk[j][k] = (float) order.number / (float) capacityMap.get(order.pName).get(k + 1);
//                } else {
//                    pjk[j][k] = M;
//                }
//            }
//        }
//
//        // 常量集合 tij：订单 i 到订单 j 之前的换洗时间
//        // 0-型号相同 1-型号不同
//        int[][] tij = new int[ordersNumber][ordersNumber];
//        // 常量集合 bcij：订单 i 到订单 j 的基料换产成本
//        // 在换产评分中以订单 j 的基料为 key，获取订单 i 基料替换 j 的评分
//        int[][] bcij = new int[ordersNumber][ordersNumber];
//        // 常量集合 fcij：订单 i 到订单 j 的填料换产成本
//        int[][] fcij = new int[ordersNumber][ordersNumber];
//        // 常量集合 rcij：订单 i 到订单 j 的填料比例换产成本
//        int[][] rcij = new int[ordersNumber][ordersNumber];
//        // 常量集合 ccij：订单 i 到订单 j 的颜色换产成本
//        int[][] ccij = new int[ordersNumber][ordersNumber];
//        // 订单换产成本 aij：订单 i 到订单 j 的换产成本
//        int[][] aij = new int[ordersNumber][ordersNumber];
//        // 换产成本权重
//        int w1 = 1;
//        int w2 = 1;
//        int w3 = 1;
//
//
//        for (int i = 0; i < ordersNumber; i++) {
//            Order orderI = orderList.get(i);
//            for (int j = 0; j < ordersNumber; j++) {
//                Order orderJ = orderList.get(j);
//                // 换产时间
//                tij[i][j] = orderI.pName.equals(orderJ.pName) ? 0 : 2;
//                // 基料换产成本：100 - 评分
//                bcij[i][j] = 100 - bmScoreMap.get(orderJ.bm).get(orderI.bm);
//                // 填料换产成本：暂时默认为 1
//                fcij[i][j] = 1;
//                // 填料比例换产成本：差的绝对值
//                rcij[i][j] = Math.abs(orderI.fmRation - orderJ.fmRation);
//                // 颜色换产成本：0-同色 1-同层 2-不同色不同层
//                if (orderI.color.equals(orderJ.color)) {
//                    ccij[i][j] = 0;
//                } else if (ValMap.colorDepthVal(orderI.color) == ValMap.colorDepthVal(orderJ.color)) {
//                    ccij[i][j] = 1;
//                } else {
//                    ccij[i][j] = 2;
//                }
//
//                // 订单换产成本：aij = w1 * bcij + w2 * fcij + w3 * ccij
//                aij[i][j] = w1 * bcij[i][j] + w2 * fcij[i][j] + w3 * ccij[i][j];
//            }
//        }
//
//        System.out.println("4. 添加约束条件 ......");
//        // 线性表达式对象
//        GRBLinExpr lineExpr;
//
//        // todo 约束条件 1 紧前订单必须唯一
//        for (int j = 0; j < ordersNumber; j++) {
//            for (int k = 0; k < linesNumber; k++) {
//                lineExpr = new GRBLinExpr();
//                for (int i = 0; i < ordersNumber; i++) {
//                    lineExpr.addTerm(1, Xijk[i][j][k]);
//                }
//                model.addConstr(lineExpr, GRB.EQUAL, Vjk[j][k], "CS1");
//            }
//        }
//
//        // 约束条件 2 紧后订单必须唯一
//        for (int i = 0; i < ordersNumber; i++) {
//            for (int k = 0; k < linesNumber; k++) {
//                lineExpr = new GRBLinExpr();
//                for (int j = 0; j < ordersNumber; j++) {
//                    lineExpr.addTerm(1.0, Xijk[i][j][k]);
//                }
//                model.addConstr(lineExpr, GRB.LESS_EQUAL, Vjk[i][k], "CS2");
//            }
//        }
//
//        // 约束条件 3 各产线上第一个订单至多有一个
//        for (int k = 0; k < linesNumber; k++) {
//            lineExpr = new GRBLinExpr();
//            for (int j = 0; j < ordersNumber; j++) {
//                lineExpr.addTerm(1, Xijk[0][j][k]);
//            }
//            model.addConstr(lineExpr, GRB.LESS_EQUAL, 1, "CS3");
//        }
//
//        // 约束条件 4 同一产线上的订单必须在前一个紧邻订单完成后生产 #if i ！= j
//        for (int i = 0; i < ordersNumber; i++) {
//            for (int j = 0; j < ordersNumber; j++) {
//                lineExpr = new GRBLinExpr();
//                lineExpr.addTerm(1, Cj[j]);
//                lineExpr.addTerm(-1, Cj[i]);
//                for (int k = 0; k < linesNumber; k++) {
//                    lineExpr.addTerm(-pjk[j][k], Vjk[j][k]);
//                    lineExpr.addTerm(-M, Xijk[i][j][k]);
//                }
//                model.addConstr(lineExpr, GRB.GREATER_EQUAL, tij[i][j] - M, "CS4");
//            }
//        }
//
//        // 约束条件 5 订单必须在原料到货后生产
//        for (int j = 0; j < ordersNumber; j++) {
//            lineExpr = new GRBLinExpr();
//            lineExpr.addTerm(1.0, Cj[j]);
//            for (int k = 0; k < linesNumber; k++) {
//                lineExpr.addTerm(-pjk[j][k], Vjk[j][k]);
//            }
//            model.addConstr(lineExpr, GRB.GREATER_EQUAL, rj[j], "CS5");
//        }
//
//        // 约束条件 6 小订单超期约束
//        // 约束条件 7 非小订单超期约束
//        for (int j = 0; j < ordersNumber; j++) {
//            if (orderList.get(j).number <= 2000) {
//                lineExpr = new GRBLinExpr();
//                lineExpr.addTerm(1.0, Cj[j]);
//                lineExpr.addTerm(-1.0, Tj[j]);
//                model.addConstr(lineExpr, GRB.LESS_EQUAL, 24 + dj[j], "CS6");
//            } else {
//                lineExpr = new GRBLinExpr();
//                lineExpr.addTerm(1.0, Cj[j]);
//                lineExpr.addTerm(-1.0, Tj[j]);
//                model.addConstr(lineExpr, GRB.LESS_EQUAL, dj[j], "CS7");
//            }
//        }
//
//        // 约束条件 8 产线分配约束
//        for (int j = 0; j < ordersNumber; j++) {
//            lineExpr = new GRBLinExpr();
//            for (int k = 0; k < linesNumber; k++) {
//                lineExpr.addTerm(ejk[j][k], Vjk[j][k]);
//            }
//            model.addConstr(lineExpr, GRB.EQUAL, 1, "CS8");
//        }
//
//
//        // 惩罚系数：暂取值为 1
//        float[] wj = new float[ordersNumber];
//        for (int j = 0; j < ordersNumber; j++) {
//            wj[j] = 1.0f;
//        }
//
//
//        System.out.println("5. 添加目标函数 ......");
//        //  将两个目标函数加权合并，暂取同权
//        lineExpr = new GRBLinExpr();
//        // 超期成本
//        for (int j = 0; j < ordersNumber; j++) {
//            lineExpr.addTerm(wj[j], Tj[j]);
//        }
//        // 换产成本
//        for (int i = 0; i < ordersNumber; i++) {
//            for (int j = 0; j < ordersNumber; j++) {
//                for (int k = 0; k < linesNumber; k++) {
//                    lineExpr.addTerm(aij[i][j], Xijk[i][j][k]);
//                }
//            }
//        }
//        model.setObjective(lineExpr, GRB.MINIMIZE);
//
//
//        // 计算
//        System.out.println("6. 开始计算 ......");
//
////        // 设置优化时间（两种设置方法，任选其一）
////        model.set("TimeLimit", "100.0");
////        model.set(GRB.DoubleParam.TimeLimit, 60 * 60 * 2);
////        设置运行时间 2h 仍没有跑出结果，因此不限制时间，跑一晚上试试看
//
//        long begin = System.currentTimeMillis();
//        model.optimize();
//        long end = System.currentTimeMillis();
//
//        // 格式化输出 创建文件 写入数据
//        LocalDateTime now = LocalDateTime.now();
//        String outputFileName = "D:\\code_workspace\\znpcserver\\src\\main\\resources\\output\\output_"
//                + now.format(DateTimeFormatter.ofPattern("yyMMdd_HHmmss"))
//                + ".txt";
//
//        File file = new File(outputFileName);
//        if (!file.exists()) {
//            file.createNewFile();
//        }
//        FileOutputStream fileOutputStream = new FileOutputStream(file);
//        OutputStreamWriter out = new OutputStreamWriter(fileOutputStream);
//
//
//        out.write("Number of orders is " + ordersNumber);
//        out.write("Number of machines is " + linesNumber);
//        out.write("Results :");
//
//
//        System.out.println("8. 格式化结果 Vjk ......");
//
//        // 输出到控制台
//        List<List<Integer>> machineOrders = ResultUtil.getMachineOrder(Xijk, ordersNumber, linesNumber);
//        DecimalFormat format = new DecimalFormat(".00");
//
//        System.out.println("Objective value : " + model.get(GRB.DoubleAttr.ObjVal));
//        System.out.println("v_jk:");
//        for (int j = 0; j < ordersNumber; j++) {
//            System.out.println("[" + (j + 1) + "]: ");
//            for (int k = 0; k < linesNumber; k++) {
//                System.out.print(String.valueOf((int) Vjk[j][k].get(GRB.DoubleAttr.X)));
//            }
//            System.out.println();
//        }
//        System.out.println();
//
//
//
//        for (int i = 0; i < machineOrders.size(); i++) {
//            System.out.println("machine [" + i + "]:");
//            List<Integer> orders = machineOrders.get(i);
//            for (int j = 0; j < orders.size(); j++) {
//                int orderId = orders.get(j);
//                System.out.print(
//                        orderId + " ("
//                                + format.format(Cj[orderId].get(GRB.DoubleAttr.X))
//                                + " "
//                                + format.format(Tj[orderId].get(GRB.DoubleAttr.X))
//                                + " ");
//
//            }
//            System.out.println();
//        }
//
//
//        // 输出到文件
//        out.write("Objective value : " + model.get(GRB.DoubleAttr.ObjVal));
//
//        out.write("v_jk:\n");
//        for (int j = 0; j < ordersNumber; j++) {
//            out.write("[" + (j + 1) + "]: ");
//            for (int k = 0; k < linesNumber; k++) {
//                out.write(String.valueOf((int) Vjk[j][k].get(GRB.DoubleAttr.X)));
//            }
//            out.write("\n");
//        }
//        out.write("\n");
//
//        for (int i = 0; i < machineOrders.size(); i++) {
//            out.write("machine [" + i + "]:");
//            List<Integer> orders = machineOrders.get(i);
//            for (int j = 0; j < orders.size(); j++) {
//                int orderId = orders.get(j);
//                out.write(
//                        orderId + " ("
//                                + format.format(Cj[orderId].get(GRB.DoubleAttr.X))
//                                + " "
//                                + format.format(Tj[orderId].get(GRB.DoubleAttr.X))
//                                + " ");
//
//            }
//            out.write("\n");
//        }
//
//        out.close();
//        fileOutputStream.close();
//        // 释放资源
//        model.dispose();
//        env.dispose();
//
//
//        /* 官方参考示例
//
//        // 决策变量
//        GRBVar x = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, "x");
//        GRBVar y = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, "y");
//        GRBVar z = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, "z");
//
//        // Set objective : maximize x + y + 2 z
//        GRBLinExpr expr = new GRBLinExpr();
//        expr.addTerm(1.0, x);
//        expr.addTerm(1.0, y);
//        expr.addTerm(2.0, z);
//        model.setObjective(expr, GRB.MAXIMIZE);
//
//
//        // Optimize model
//        model.optimize();
//        System.out.println(x.get(GRB.StringAttr.VarName)
//                + " " + x.get(GRB.DoubleAttr.X));
//        System.out.println(y.get(GRB.StringAttr.VarName)
//                + " " + y.get(GRB.DoubleAttr.X));
//        System.out.println(z.get(GRB.StringAttr.VarName)
//                + " " + z.get(GRB.DoubleAttr.X));
//        System.out.println("Obj : " + model.get(GRB.DoubleAttr.ObjVal));
//
//        // Dispose of model and environment
//        model.dispose();
//        env.dispose();
//        */
//
//    }

}
